#include "DXUT.h"
#include "SDKmisc.h"
#include "DXUTcamera.h"
#include "Curve3DRender.h"
#include "..\Rendering\VertexFormat.h"
extern Curve3DRender*				g_pCurve3DRender;

extern LPDIRECT3DTEXTURE9			g_pRenderTexture1;
extern LPDIRECT3DSURFACE9			g_pRenderSurface1;
extern LPDIRECT3DTEXTURE9			g_pRenderTexture2;
extern LPDIRECT3DSURFACE9			g_pRenderSurface2;
extern LPDIRECT3DVERTEXBUFFER9		MCVB;
extern LPDIRECT3DINDEXBUFFER9		MCIB;
extern unsigned int					totalfacts;
extern bool							isAccurateMC;
extern unsigned int					totalverts;//if isAccurateMC=false totalverts = 400000;

extern LPDIRECT3DVERTEXBUFFER9		CurveVB;
extern int							curvePointNum;

//Defined in "Definition.h"
#if DXUTMODLEVIEWCAMERA
extern CModelViewerCamera   g_Camera;
#else
extern CFirstPersonCamera	g_Camera;
#endif


extern void CurveRender3D(RENDER_PARAM* renderParam, int PointRadius, bool IsForeshorten, bool BackCulling, bool BackDifferent);

Curve3DRender::Curve3DRender(void)
{
	iConstantTerm = 350;
	iPointRadius = 10;
	bShowCurve3DDiag = true;
	bIsForeshorten = false;
	bBackCulling   = false;
	bBackDifferent = false;

	bMCCorner = true;
	bMCCurveOnly  = false;
	fIsoEnlarge=5.0f;
	fBiggestIso=0.6f;
	fOverlapIso=0.1f;
}

Curve3DRender::~Curve3DRender(void)
{
	SAFE_RELEASE(pMCPointEffect);
	SAFE_RELEASE(pPointEffect);
}

void Curve3DRender::CreateGUI(CDXUTDialogResourceManager* pManager)
{
	Curve3DDiag.Init(pManager);
	Curve3DDiag.SetCallback(OnCurve3DGUIEvent);
	Curve3DDiag.SetVisible(bShowCurve3DDiag);
	Curve3DDiag.EnableCaption(true);
	Curve3DDiag.SetCaptionText(L"3D curve render");

	int iY = 0; 
	WCHAR sz[100];

	Curve3DDiag.AddCheckBox(IDC_ISFORESHORTEN, L"Foreshorten",  20, iY , 180, 16, bIsForeshorten);
	Curve3DDiag.AddCheckBox(IDC_BACKCULLINT,   L"Back culling", 20, iY += 20, 180, 16, bBackCulling);
	Curve3DDiag.AddCheckBox(IDC_BACKDIFFERENT, L"Front Vs. back", 20, iY += 20, 180, 16, bBackDifferent);
	Curve3DDiag.AddStatic(IDC_GEOMETRICCONSTRAIN_STATIC, L"Constrain: ", 0, iY += 20, 60, 20, false);
	Curve3DDiag.AddEditBox(IDC_GEOMETRICCONSTRAIN, L"x^2+y^2+z^2-C=0", 20, iY+=20, 150, 30, true);
	StringCchPrintf( sz, 100, L"Constant: %3d", iConstantTerm ); 
	Curve3DDiag.AddStatic(IDC_CONSTANTTERM_STATIC, sz, 0, iY += 30, 70, 20, false);	
	Curve3DDiag.AddSlider(IDC_CONSTANTTERM, 70, iY, 100, 20, -50, 1000, iConstantTerm);
	StringCchPrintf( sz, 100, L"PRadius: %2d", iPointRadius ); 
	Curve3DDiag.AddStatic(IDC_POINTRADIUS_STATIC, sz, 0, iY +=20, 70, 20, false);
	Curve3DDiag.AddSlider(IDC_POINTRADIUS, 70, iY, 100, 20, 1, 64, iPointRadius);
	
	iY += 30;
	Curve3DDiag.AddCheckBox(IDC_MCCURVEONLY, L"MC Curve iso", 0, iY+=20, 100, 16, bMCCurveOnly);
	Curve3DDiag.AddCheckBox(IDC_MCCORNER, L"MC Corner", 100, iY, 80, 16, bMCCorner);
	StringCchPrintf(sz, 100, L"Enlarge: %2.1f", fIsoEnlarge);
	Curve3DDiag.AddStatic(IDC_ENLARGE_STATIC, sz, 0, iY+=30, 70, 20);
	Curve3DDiag.AddSlider(IDC_ENLARGE, 70, iY, 100, 20, 1, 100, fIsoEnlarge*5);
	StringCchPrintf(sz, 100, L"Biggest: %2.1f", fBiggestIso);
	Curve3DDiag.AddStatic(IDC_BIGGESTISO_STATIC, sz, 0, iY+=30, 70, 20);
	Curve3DDiag.AddSlider(IDC_BIGGESTISO, 70, iY, 100, 20, 1, 100, fBiggestIso*50);
	StringCchPrintf(sz, 100, L"Overlap: %2.1f", fOverlapIso);
	Curve3DDiag.AddStatic(IDC_OVERLAPISO_STATIC, sz, 0, iY+=30, 70, 20);
	Curve3DDiag.AddSlider(IDC_OVERLAPISO, 70, iY, 100, 20, 1, 100, fOverlapIso*100);
}

void CALLBACK OnCurve3DGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext )
{
	if(nControlID<=IDC_CURVE3D_MIN||nControlID>=IDC_CURVE3D_MAX)	return;

	WCHAR sz[100];
	switch (nControlID)
	{
	case IDC_ISFORESHORTEN:
		g_pCurve3DRender->bIsForeshorten = g_pCurve3DRender->Curve3DDiag.GetCheckBox(IDC_ISFORESHORTEN)->GetChecked();
		break;
	case IDC_BACKCULLINT:
		g_pCurve3DRender->bBackCulling = g_pCurve3DRender->Curve3DDiag.GetCheckBox(IDC_BACKCULLINT)->GetChecked();
		break;
	case IDC_BACKDIFFERENT:
		g_pCurve3DRender->bBackDifferent = g_pCurve3DRender->Curve3DDiag.GetCheckBox(IDC_BACKDIFFERENT)->GetChecked();
		break;
	case IDC_CONSTANTTERM:
		g_pCurve3DRender->iConstantTerm = g_pCurve3DRender->Curve3DDiag.GetSlider(IDC_CONSTANTTERM)->GetValue();
		StringCchPrintf(sz, 100, L"Constant: %d", g_pCurve3DRender->iConstantTerm);
		g_pCurve3DRender->Curve3DDiag.GetStatic(IDC_CONSTANTTERM_STATIC)->SetText(sz);
		break;
	case IDC_POINTRADIUS:
		g_pCurve3DRender->iPointRadius = g_pCurve3DRender->Curve3DDiag.GetSlider(IDC_POINTRADIUS)->GetValue();
		StringCchPrintf(sz, 100, L"PRadius: %d", g_pCurve3DRender->iPointRadius);
		g_pCurve3DRender->Curve3DDiag.GetStatic(IDC_POINTRADIUS_STATIC)->SetText(sz);
		break;
	case IDC_MCCURVEONLY:
		g_pCurve3DRender->bMCCurveOnly = g_pCurve3DRender->Curve3DDiag.GetCheckBox(IDC_MCCURVEONLY)->GetChecked();
		break;
	case IDC_MCCORNER:
		g_pCurve3DRender->bMCCorner = g_pCurve3DRender->Curve3DDiag.GetCheckBox(IDC_MCCORNER)->GetChecked();
		break;
	case IDC_ENLARGE:
		g_pCurve3DRender->fIsoEnlarge = g_pCurve3DRender->Curve3DDiag.GetSlider(IDC_ENLARGE)->GetValue()/5.0f;
		StringCchPrintf(sz, 100, L"Enlarge: %2.1f", g_pCurve3DRender->fIsoEnlarge);
		g_pCurve3DRender->Curve3DDiag.GetStatic(IDC_ENLARGE_STATIC)->SetText(sz);
		break;
	case IDC_BIGGESTISO:
		g_pCurve3DRender->fBiggestIso = g_pCurve3DRender->Curve3DDiag.GetSlider(IDC_BIGGESTISO)->GetValue()/50.0f;
		StringCchPrintf(sz, 100, L"Biggest: %2.1f", g_pCurve3DRender->fBiggestIso);
		g_pCurve3DRender->Curve3DDiag.GetStatic(IDC_BIGGESTISO_STATIC)->SetText(sz);
		break;
	case IDC_OVERLAPISO:
		g_pCurve3DRender->fOverlapIso = g_pCurve3DRender->Curve3DDiag.GetSlider(IDC_OVERLAPISO)->GetValue()/100.0f;
		StringCchPrintf(sz, 100, L"Overlap: %2.1f", g_pCurve3DRender->fOverlapIso);
		g_pCurve3DRender->Curve3DDiag.GetStatic(IDC_OVERLAPISO_STATIC)->SetText(sz);
		break;
	}
}


HRESULT Curve3DRender::OnCreateDevice( LPDIRECT3DDEVICE9 pd3dDevice )
{
	WCHAR str[MAX_PATH];
	HRESULT	hr;
	pD3DDev = pd3dDevice;

	V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, L"./Shader.fxo" ) );
	V_RETURN( D3DXCreateEffectFromFile( pD3DDev, str, NULL, NULL, NULL, NULL, &pMCPointEffect, NULL ) );

	V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, L"./WireFrame.fxo" ) );
	V_RETURN( D3DXCreateEffectFromFile( pD3DDev, str, NULL, NULL, NULL, NULL, &pPointEffect, NULL ) );

	return S_OK;

}

void Curve3DRender::OnRender(float fElapsedTime, IDirect3DStateBlock9* pStateBlock, LPDIRECT3DTEXTURE9 pTexGaussian, ABSSf* pABS, RENDER_PARAM* renderParam)
{
	HRESULT hr;
	UINT cPasses;
	LPDIRECT3DSURFACE9	pOldSurface;
	V( pD3DDev->GetRenderTarget(0, &pOldSurface) );


	V(pStateBlock->Capture());

	float box0[3] = {pABS->m_ABez->box[0][0],        pABS->m_ABez->box[1][0],        pABS->m_ABez->box[2][0]};
	float boxSize[3] = {pABS->m_ABez->box[0][1]-box0[0], pABS->m_ABez->box[1][1]-box0[1], pABS->m_ABez->box[2][1]-box0[2]};	
	float eye[3] = {renderParam->eye[0], renderParam->eye[1], renderParam->eye[2]};
	//MATERIAL_TRANS_FX material;
	//GetFxMaterial(&material, renderParam->material);
	//material.use3DTexture = renderParam->material.use3DTexture;
	//material.singleColor = renderParam->material.singleColor;
	//material.ka = renderParam->material.ka;
	//material.kd = renderParam->material.kd;
	//material.ks = renderParam->material.ks;
	//material.n = renderParam->material.n;
	//material.t = renderParam->material.t;
	//float denominator = 1.0f/255.0f;
	//material.a[0] = (renderParam->material.a<<24>>24)*denominator;
	//material.a[1] = (renderParam->material.a<<16>>24)*denominator;
	//material.a[2] = (renderParam->material.a<< 8>>24)*denominator;
	//material.s[0] = (renderParam->material.s<<24>>24)*denominator;
	//material.s[1] = (renderParam->material.s<<16>>24)*denominator;
	//material.s[2] = (renderParam->material.s<< 8>>24)*denominator;
	//for (int i=0; i<5; i++)
	//{
	//	material.d[3*i  ] = (renderParam->material.d[i]<<24>>24)*denominator;
	//	material.d[3*i+1] = (renderParam->material.d[i]<<16>>24)*denominator;
	//	material.d[3*i+2] = (renderParam->material.d[i]<< 8>>24)*denominator;		
	//}

	D3DXMATRIXA16 mWorld;
#if DXUTMODLEVIEWCAMERA
	mWorld = *g_Camera.GetWorldMatrix();
#else
	D3DXMatrixIdentity(&mWorld);
#endif
	D3DXMATRIXA16 mWorldViewProj = mWorld * *g_Camera.GetViewMatrix() * *g_Camera.GetProjMatrix();

	V( pMCPointEffect->SetMatrix( "g_mWorldViewProjection", &mWorldViewProj ) );	
 	V( pMCPointEffect->SetValue("material", &renderParam->material, sizeof(MATERIAL)) );
 	V( pMCPointEffect->SetValue("light", &(renderParam->light), sizeof(LIGHT)*3) );
	V( pMCPointEffect->SetValue("eye", &eye, sizeof(float)*3) );
	V( pMCPointEffect->SetValue("box0", box0, sizeof(float)*3) );
	V( pMCPointEffect->SetValue("boxSize", &boxSize, sizeof(float)*3) );
 	V( pMCPointEffect->SetInt("nLight", (renderParam->nLight)) );
	V( pMCPointEffect->SetValue("iteration", &iConstantTerm, sizeof(int)) );
	V( pMCPointEffect->SetValue("segNum", &pABS->m_ABez->seg, sizeof(int)*3) );
	//V( pMCPointEffect->SetValue("bSingleColor", &(renderParam->material.singleColor), sizeof(int)) );
	V( pMCPointEffect->SetValue("isoEnlarge", &fIsoEnlarge, sizeof(float)) );
	V( pMCPointEffect->SetValue("biggestIso", &fBiggestIso, sizeof(float)) );
	V( pMCPointEffect->SetValue("OverlapIso", &fOverlapIso, sizeof(float)) );

	int MCCurveOnly = bMCCurveOnly;
	V( pMCPointEffect->SetValue("MCCurveOnly", &MCCurveOnly, sizeof(int)) );

#if MC_WITH_NORMAL
	V( pD3DDev->SetStreamSource( 0, MCVB, 0, sizeof(POINTVERTEXNORMAL) ) );
	V( pD3DDev->SetFVF( D3DFVF_XYZ|D3DFVF_NORMAL ) );
#else
	V( pD3DDev->SetStreamSource( 0, MCVB, 0, sizeof(POINTVERTEX) ) );
	V( pD3DDev->SetFVF( D3DFVF_XYZ ) );
#endif

	if(isAccurateMC)
		V( pD3DDev->SetIndices(MCIB) );

	D3DVIEWPORT9 viewPort;
	V( pD3DDev->GetViewport(&viewPort) );
	viewPort.Width = renderParam->renderWidth;
	viewPort.Height = renderParam->renderHeight;

	V(pD3DDev->SetRenderTarget(0, g_pRenderSurface1));
	V(pD3DDev->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DXCOLOR(0.0f,0.0f,0.0f, 0.0f), 1.0f, 0));
	V(pD3DDev->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE ));
	V(pD3DDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE));

	V( pMCPointEffect->SetTechnique( "MCHalfSpace" ) );
	pMCPointEffect->CommitChanges();
	V( pMCPointEffect->Begin(&cPasses, 0) );
	V( pMCPointEffect->BeginPass(0) );
	if(isAccurateMC)
	{
		V( pD3DDev->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, 0, totalverts, 0, totalfacts ) );
		//V( pD3DDev->DrawPrimitive( D3DPT_POINTLIST, 0, totalverts) );
	}
	else
	{
		V( pD3DDev->DrawPrimitive( D3DPT_TRIANGLELIST, 0, totalverts/3 ) );
	}
	V( pMCPointEffect->EndPass() );
	V( pMCPointEffect->End() );

	if (bMCCurveOnly)
	{
		V(pD3DDev->SetRenderTarget(0, pOldSurface));
		V( pD3DDev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DXCOLOR(1.0f,1.0f,1.0f, 0.0f), 1.0f, 0) );
	}
	else
	{
		V(pD3DDev->SetRenderTarget(0, g_pRenderSurface2));
		V( pD3DDev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DXCOLOR(0.0f,0.0f,0.0f, 0.0f), 1.0f, 0) );
	}
	PPVERT quad[4] =
	{
		{ -0.5f,								-0.5f,								0.99f, 1.0f, 0.0f, 0.0f},
		{ renderParam->renderWidth - 0.5f,		-0.5f,								0.99f, 1.0f, 1.0f, 0.0f},
		{ -0.5f,								renderParam->renderHeight - 0.5f,	0.99f, 1.0f, 0.0f, 1.0f},
		{ renderParam->renderWidth - 0.5f,		renderParam->renderHeight - 0.5f,	0.99f, 1.0f, 1.0f, 1.0f}
	};
	V( pD3DDev->SetFVF( D3DFVF_PPVERT ) );
	V( pMCPointEffect->SetTexture("g_txSrcColor", g_pRenderTexture1) );
	V( pMCPointEffect->SetTechnique("IsoBandEdge") );	
	V( pMCPointEffect->CommitChanges() );
	V( pMCPointEffect->Begin( &cPasses, 0 ) );
	V( pMCPointEffect->BeginPass( 0 ) );
	V( pD3DDev->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP, 2, quad, sizeof(PPVERT) ) );
	V( pMCPointEffect->EndPass() );
	V( pMCPointEffect->End() );

	if (!bMCCurveOnly)
	{
		CurveRender3D(renderParam, iPointRadius, bIsForeshorten, bBackCulling, bBackDifferent);
		V(pD3DDev->SetRenderTarget(0, pOldSurface));
		V( pD3DDev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DXCOLOR(1.0f,1.0f,1.0f, 0.0f), 1.0f, 0) );
		V(pD3DDev->SetStreamSource( 0, CurveVB, 0, sizeof(SCREENBLENDVERTEX) ));
		V(pD3DDev->SetFVF( D3DFVF_SCREENBLENDVERTEX ));

		pD3DDev->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
		pD3DDev->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
		pD3DDev->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );

		V( pPointEffect->SetTechnique( "ControlPoint" ) );
		V( pPointEffect->SetTexture( "TexGaussian", pTexGaussian ) );

		V( pPointEffect->Begin(&cPasses, 0) );
		V( pPointEffect->BeginPass(0) );
		pD3DDev->DrawPrimitive( D3DPT_TRIANGLELIST, 0, curvePointNum*2 );
		V( pPointEffect->EndPass() );
		V( pPointEffect->End() );
	}

	  /************************************************************************/
	  /* Shading in corner                                                    */
	  /************************************************************************/
	if(bMCCorner)
	{
	#if MC_WITH_NORMAL
		V( pD3DDev->SetStreamSource( 0, MCVB, 0, sizeof(POINTVERTEXNORMAL) ) );
		V( pD3DDev->SetFVF( D3DFVF_XYZ|D3DFVF_NORMAL ) );
	#else
		V( pD3DDev->SetStreamSource( 0, MCVB, 0, sizeof(POINTVERTEX) ) );
		V( pD3DDev->SetFVF( D3DFVF_XYZ ) );
	#endif
		if(isAccurateMC)		V( pD3DDev->SetIndices(MCIB) );

		//V(pD3DDev->SetRenderTarget(0, pOldSurface));
		viewPort.Width = renderParam->renderWidth/3;
		viewPort.Height = renderParam->renderHeight/3;
		V( pD3DDev->SetViewport(&viewPort) );
		V(pD3DDev->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE ));
		V(pD3DDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE));
		V(pD3DDev->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE ));

		V( pMCPointEffect->SetTechnique( "RenderShadingPhong" ) );
		V( pMCPointEffect->CommitChanges() );

		V( pMCPointEffect->Begin(&cPasses, 0) );
		V( pMCPointEffect->BeginPass(0) );
		if(isAccurateMC)
		{
			V( pD3DDev->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, 0, totalverts, 0, totalfacts ) );
			//V( pD3DDev->DrawPrimitive( D3DPT_POINTLIST, 0, totalverts) );
		}
		else
		{
			V( pD3DDev->DrawPrimitive( D3DPT_TRIANGLELIST, 0, totalverts/3 ) );
		}
		V( pMCPointEffect->EndPass() );
		V( pMCPointEffect->End() );
	}

	viewPort.Width = renderParam->renderWidth;
	viewPort.Height = renderParam->renderHeight;
	V( pD3DDev->SetViewport(&viewPort) );
	V(Curve3DDiag.OnRender(fElapsedTime));

	V(pStateBlock->Apply());

	SAFE_RELEASE(pOldSurface);
}

bool Curve3DRender::HandleMessages( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	if(Curve3DDiag.MsgProc(hWnd, uMsg, wParam, lParam))
		return true;
	return false;
}
HRESULT Curve3DRender::OnResetDevice(const D3DSURFACE_DESC* pBackBufferSurfaceDesc)
{
	HRESULT hr;
	D3DXHANDLE hParamToConvert;
	D3DXHANDLE hAnnotation;
	UINT uParamIndex = 0;
	// If a top-level parameter has the "ConvertPixelsToTexels" annotation,
	// do the conversion.
	while( NULL != ( hParamToConvert = pMCPointEffect->GetParameter( NULL, uParamIndex++ ) ) )
	{
		if( NULL != ( hAnnotation = pMCPointEffect->GetAnnotationByName( hParamToConvert, "ConvertPixelsToTexels" ) ) )
		{
			LPCSTR szSource;
			pMCPointEffect->GetString( hAnnotation, &szSource );
			D3DXHANDLE hConvertSource = pMCPointEffect->GetParameterByName( NULL, szSource );

			if( hConvertSource )
			{
				// Kernel source exists. Proceed.
				// Retrieve the kernel size
				D3DXPARAMETER_DESC desc;
				pMCPointEffect->GetParameterDesc( hConvertSource, &desc );
				// Each element has 2 floats
				DWORD cKernel = desc.Bytes / (2 * sizeof(float));
				D3DXVECTOR4 *pvKernel = new D3DXVECTOR4[cKernel];
				if( !pvKernel )
					return E_OUTOFMEMORY;
				pMCPointEffect->GetVectorArray( hConvertSource, pvKernel, cKernel );
				// Convert
				for( DWORD i = 0; i < cKernel; ++i )
				{
					pvKernel[i].x = pvKernel[i].x / pBackBufferSurfaceDesc->Width;
					pvKernel[i].y = pvKernel[i].y / pBackBufferSurfaceDesc->Height;
				}
				// Copy back
				pMCPointEffect->SetVectorArray( hParamToConvert, pvKernel, cKernel );

				delete[] pvKernel;
			}
		}
	}

	V_RETURN( pMCPointEffect->OnResetDevice() );
	V_RETURN( pPointEffect->OnResetDevice() );
	
	SetGUILocationSize(pBackBufferSurfaceDesc->Width-200, pBackBufferSurfaceDesc->Height-500, 200, 420);

	return hr;

}
void Curve3DRender::OnLostDevice()
{
	pMCPointEffect->OnLostDevice();
	pPointEffect->OnLostDevice();

}